#include <qfiledialog.h>
#include <QSortFilterProxyModel>
#include <qmessagebox.h>
#include <qevent.h>
#include <qprogressdialog.h>

#include "audiosortfilterproxy.h"
#include "tagreaderthread.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "debugmodule.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    m_settings("settings.ini", QSettings::IniFormat),
    m_pPlaylistsProxy(NULL)
{
    ui->setupUi(this);

    m_pLblStatus = new QLabel(this);
    ui->statusBar->addPermanentWidget(m_pLblStatus);

    // Kontextmenüs
    ui->tblAudioFiles->addAction(ui->actionReadTags);

    ui->tblPlaylists->addAction(ui->actionRemovePlaylist);
    ui->tblPlaylists->addAction(ui->actionRemoveAllPlaylists);

    ui->tblEntries->addAction(ui->actionRefreshId3);
    ui->tblEntries->addAction(ui->actionRemoveEntry);

    loadSettings();

    // Audiodateien
    loadAudioFiles();

    m_pFilesProxy = new AudioSortFilterProxy(this);
    m_pFilesProxy->setSortLocaleAware(true);
    m_pFilesProxy->setSourceModel(&m_modelAudioFiles);
    m_pFilesProxy->setFilterKeyColumn(0);
    m_pFilesProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
    ui->tblAudioFiles->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    ui->tblAudioFiles->setModel(m_pFilesProxy);
    //ui->tblAudioFiles->sortByColumn(ModelAudioFiles::NoOfPlaylistEntries, Qt::AscendingOrder);
    ui->tblAudioFiles->sortByColumn(m_settings.value("SortColumnFiles", ModelAudioFiles::NoOfPlaylistEntries).toInt(),
                                    static_cast<Qt::SortOrder>(m_settings.value("SortOrderFiles", Qt::AscendingOrder).toInt()));
    //ui->tblAudioFiles->resizeColumnsToContents();
    connect(ui->edtFilter, SIGNAL(textChanged(QString)), m_pFilesProxy, SLOT(setFilterWildcard(QString)));
    connect(ui->tblAudioFiles->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
            SLOT(filesSelected()));

    // Playlisten
    connect(&m_modelPlaylists, SIGNAL(countChanged(int)), SLOT(showListsCount(int)));
    loadPlaylists();

    m_pPlaylistsProxy = new QSortFilterProxyModel(this);
    m_pPlaylistsProxy->setSortLocaleAware(true);
    m_pPlaylistsProxy->setSourceModel(&m_modelPlaylists);
    m_pPlaylistsProxy->setFilterKeyColumn(0);
    ui->tblPlaylists->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    ui->tblPlaylists->setModel(m_pPlaylistsProxy);
    ui->tblPlaylists->sortByColumn(0, Qt::AscendingOrder);
    //ui->tblPlaylists->resizeColumnsToContents();
    connect(ui->tblPlaylists->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
            SLOT(showEntries()));

    // Playlisteinträge
    m_pEntriesProxy = new QSortFilterProxyModel(this);
    m_pEntriesProxy->setSortLocaleAware(true);
    m_pEntriesProxy->setSourceModel(&m_modelPlaylistEntries);
    m_pEntriesProxy->setFilterKeyColumn(0);
    ui->tblEntries->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    ui->tblEntries->setModel(m_pEntriesProxy);
    ui->tblEntries->sortByColumn(0, Qt::AscendingOrder);
    connect(ui->tblEntries->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
            SLOT(entriesSelected()));
    connect(&m_modelPlaylistEntries, SIGNAL(countChanged(int)), SLOT(showEntriesCount(int)));

    // Thread
    m_pPrgCancelButton = new QPushButton("Abbrechen", this);
    m_pPrgDialog = new QProgressDialog(this, Qt::MSWindowsFixedSizeDialogHint);
    m_pPrgDialog->setCancelButton(m_pPrgCancelButton);
    m_pPrgDialog->setWindowModality(Qt::WindowModal);
    Playlist::sm_pTagThread = m_pTagThread = new TagReaderThread(this);
    connect(m_pTagThread, SIGNAL(started()), SLOT(threadStarted()));
    connect(m_pTagThread, SIGNAL(finished()), SLOT(threadFinished()));
    connect(m_pTagThread, SIGNAL(progressChanged(int,int)), SLOT(showThreadProgress(int,int)));
    connect(m_pPrgDialog, SIGNAL(canceled()), SLOT(stopThread()));
    disconnect(m_pPrgDialog, SIGNAL(canceled()), m_pPrgDialog, SLOT(cancel()));

    //TODO: Duplikate
    //TODO: Translation kann unter Linux nicht geladen werden
}

MainWindow::~MainWindow()
{
    saveSettings();

    delete ui;
}

void MainWindow::closeEvent(QCloseEvent* e)
{
    foreach (Playlist* pList, m_modelPlaylists.lists())
    {
        if (checkChanges(pList))
        {
            e->ignore();
            return;
        }
    }

    // Fenstergeometrie speichern
    m_settings.setValue("geometry", saveGeometry());
    m_settings.setValue("splitterLeftRight", ui->splitterLeftRight->saveState());
    m_settings.setValue("splitterUpDown", ui->splitterUpDown->saveState());

    e->accept();
}

void MainWindow::loadSettings()
{
    ui->edtReplaceMusicPath->setText(m_settings.value("MusicPathCF", QString()).toString());
    ui->edtMusicPath->setText(m_settings.value("MusicPathLocal", QString()).toString());

    // Fenstergeometrie laden
    restoreGeometry(m_settings.value("geometry").toByteArray());
    ui->splitterLeftRight->restoreState(m_settings.value("splitterLeftRight").toByteArray());
    ui->splitterUpDown->restoreState(m_settings.value("splitterUpDown").toByteArray());
}

void MainWindow::saveSettings()
{
    m_settings.setValue("MusicPathCF", Playlist::sm_replacePath);
    m_settings.setValue("MusicPathLocal", ui->edtMusicPath->text());

    m_settings.beginWriteArray("Playlistfiles");

    int idx = 0;
    foreach (const Playlist* list, m_modelPlaylists.lists())
    {
        if (list->fileInfo().exists())
        {
            m_settings.setArrayIndex(idx++);
            m_settings.setValue("File", list->fileInfo().absoluteFilePath());
        }
    }

    m_settings.endArray();

    // Sortierungen
    m_settings.setValue("SortColumnFiles", m_pFilesProxy->sortColumn());
    m_settings.setValue("SortOrderFiles", m_pFilesProxy->sortOrder());
}

void MainWindow::changeMusicPath()
{
    const QString& newDir = QFileDialog::getExistingDirectory(
                this, "Musikpfad auswählen", ui->edtMusicPath->text());

    if (!newDir.isEmpty())
    {
        ui->edtMusicPath->setText(newDir);
        loadAudioFiles();
    }
}

void MainWindow::openPlaylists()
{
    const QStringList& files = QFileDialog::getOpenFileNames(
                this, "Playlisten öffnen", QString(),
                "Playlisten (*.m3u *.mpl);;M3U-Playlisten (*.m3u);;MPL-Playlisten (*.mpl)");

    foreach (const QString& file, files)
    {
        if (!m_modelPlaylists.addFile(file, m_modelAudioFiles.filesHash()))
        {
            QMessageBox::warning(this, QString(),
                                 QString("Playlist konnte nicht geöffnet werden:\n%1\nDatei bereits in Liste?").
                                 arg(file));
        }
    }

    const int listCount = m_modelPlaylists.lists().count();
    //ui->tblPlaylists->resizeColumnsToContents();
    ui->actionRemoveAllPlaylists->setEnabled(listCount > 0);
}

void MainWindow::showEntries()
{
    if (m_pPlaylistsProxy == NULL)
        return;

    const QModelIndexList& rows = ui->tblPlaylists->selectionModel()->selectedRows();

    if (rows.size() == 1)
    {
        const int idx = m_pPlaylistsProxy->mapToSource(rows.at(0)).row();
        Playlist* pPlaylist = m_modelPlaylists.lists().at(idx);
        m_modelPlaylistEntries.show(pPlaylist);
    }
    else
    {
        m_modelPlaylistEntries.show(NULL);
    }

    //ui->tblEntries->resizeColumnsToContents();

    // Actions (de)aktivieren
    const bool selected = !rows.isEmpty();
    ui->actionRemovePlaylist->setEnabled(selected);
    ui->actionSave->setEnabled(selected);
    ui->actionSaveAs->setEnabled(selected);

    entriesSelected();
}

void MainWindow::enableReplacePath()
{
    ui->btnApplyReplace->setEnabled(true);
}

void MainWindow::setReplacePath()
{
    ui->btnApplyReplace->setEnabled(false);
    m_modelPlaylists.setReplacePath(ui->edtReplaceMusicPath->text(), m_modelAudioFiles.filesHash());
}

void MainWindow::createPlaylist()
{
    const QString& name = QFileDialog::getSaveFileName(this, "Playliste erstellen", QString(),
                                                       "MPL-Playlisten (*.mpl);;M3U-Playlisten (*.m3u)");

    if (!name.isEmpty())
    {
        m_modelPlaylists.addNewFile(name);
    }
}

void MainWindow::removePlaylist()
{
    const QModelIndexList& rows = ui->tblPlaylists->selectionModel()->selectedRows();

    // Selektierte erst absteigend nach row sortieren,
    // dann kann von hinten her gelöscht werden und die Indizes verschieben sich nicht
    QList<int> indexes;
    foreach(const QModelIndex& mi, rows)
       indexes << m_pPlaylistsProxy->mapToSource(mi).row();

    qSort(indexes);

    for(int i = indexes.size() - 1; i > -1; i--)
    {
        if (checkChanges(m_modelPlaylists.lists().at(indexes.at(i))))
            break;

        m_modelPlaylists.remove(indexes.at(i));
    }

    if (m_modelPlaylists.lists().isEmpty())
        ui->actionRemoveAllPlaylists->setEnabled(false);
}

void MainWindow::removeAllPlaylists()
{
    foreach (Playlist* pList, m_modelPlaylists.lists())
    {
        if (checkChanges(pList))
            return;
    }

    ui->tblPlaylists->clearSelection();
    m_modelPlaylists.clear();
    ui->actionRemoveAllPlaylists->setEnabled(false);
}

void MainWindow::savePlaylists()
{
    saveSelectedPlaylists(false);
}

void MainWindow::savePlaylistsAs()
{
    saveSelectedPlaylists(true);
}

void MainWindow::entriesSelected()
{
    const QModelIndexList& rows = ui->tblEntries->selectionModel()->selectedRows();
    const bool selected = !rows.isEmpty();

    // Actions (de)aktivieren
    ui->actionRemoveEntry->setEnabled(selected);
    ui->actionRefreshId3->setEnabled(selected);
}

void MainWindow::removeEntry()
{
    const QModelIndexList& rows = ui->tblEntries->selectionModel()->selectedRows();

    // Selektierte erst absteigend nach row sortieren,
    // dann kann von hinten her gelöscht werden und die Indizes verschieben sich nicht
    QList<int> indexes;
    foreach(const QModelIndex& mi, rows)
       indexes << m_pEntriesProxy->mapToSource(mi).row();

    qSort(indexes);

    for(int i = indexes.size() - 1; i > -1; i--)
    {
        m_modelPlaylistEntries.remove(indexes.at(i));
    }
}

void MainWindow::refreshEntryId3()
{
    const QModelIndexList& rows = ui->tblEntries->selectionModel()->selectedRows();
    const QList<PlaylistEntry*>& entries = m_modelPlaylistEntries.entries();
    QList<PlaylistEntry*> entriesToRefresh;

    foreach(const QModelIndex& mi, rows)
    {
        entriesToRefresh << entries.at(m_pEntriesProxy->mapToSource(mi).row());
    }

    m_pTagThread->refreshEntries(entriesToRefresh);
}

void MainWindow::showEntriesCount(int i_count)
{
    QString text;

    if (i_count > 0)
    {
        text = QString("Playlisteinträge für '%1' (%2)").
                arg(m_modelPlaylistEntries.playlist()->filename()).arg(i_count);
    }
    else
        text = "Playlisteinträge";

    ui->grpEntries->setTitle(text);
}

void MainWindow::showListsCount(int i_count)
{
    ui->grpLists->setTitle(QString("Playlisten (%1)").arg(i_count));
}

void MainWindow::readTags()
{
    const QModelIndexList& rows = ui->tblAudioFiles->selectionModel()->selectedRows();
    const QList<AudioFile*>& files = m_modelAudioFiles.files();
    QList<AudioFile*> filesToRead;

    foreach (const QModelIndex& mi, rows)
    {
        filesToRead << files.at(m_pFilesProxy->mapToSource(mi).row());
    }

    m_pTagThread->readTags(filesToRead);
}

void MainWindow::filesSelected()
{
    const bool selected = !ui->tblAudioFiles->selectionModel()->selectedRows().isEmpty();
    ui->actionReadTags->setEnabled(selected);
}

void MainWindow::threadStarted()
{
    ui->tblAudioFiles->setUpdatesEnabled(false);
    ui->tblEntries->setUpdatesEnabled(false);
    ui->tblPlaylists->setUpdatesEnabled(false);
    m_pPrgCancelButton->setEnabled(true);
    m_pPrgDialog->setLabelText("Taginformationen werden abgerufen...");
    m_pPrgDialog->setValue(0);
    m_pPrgDialog->show();
}

void MainWindow::threadFinished()
{
    ui->tblAudioFiles->setUpdatesEnabled(true);
    ui->tblEntries->setUpdatesEnabled(true);
    ui->tblPlaylists->setUpdatesEnabled(true);
    ui->statusBar->showMessage("Taginformationen wurden abgerufen.");
    m_pPrgDialog->reset();
}

void MainWindow::showThreadProgress(int i_done, int i_count)
{
    ui->statusBar->showMessage(
                QString("Taginformationen werden abgerufen (%1 von %2)...").
                arg(i_done).arg(i_count));
    m_pPrgDialog->setMaximum(i_count);
    m_pPrgDialog->setValue(i_done);
}

void MainWindow::stopThread()
{
    m_pPrgCancelButton->setDisabled(true);
    m_pPrgDialog->setLabelText("Wird abgebrochen...");
    m_pTagThread->requestInterruption();
}

void MainWindow::loadAudioFiles()
{
    // Verzeichnisinhalt neu abrufen
    m_pLblStatus->setText("Audiodateien werden aufgelistet...");

    const int noOfFiles = m_modelAudioFiles.loadFiles(ui->edtMusicPath->text());

    m_pLblStatus->setText(QString("%1 Audiodateien geladen.").arg(noOfFiles));
    ui->grpFiles->setTitle(QString("Audiodateien (%1)").arg(noOfFiles));
    //ui->tblAudioFiles->resizeColumnsToContents();

    // Playlisten aktualisieren
    m_modelPlaylists.setLocalPath(ui->edtMusicPath->text(), m_modelAudioFiles.filesHash());
    showEntries();
}

void MainWindow::loadPlaylists()
{
    setReplacePath();

    const int size = m_settings.beginReadArray("Playlistfiles");
    const QHash<QString, AudioFile*>& filesHash = m_modelAudioFiles.filesHash();

    for (int i = 0; i < size; i++)
    {
        m_settings.setArrayIndex(i);
        m_modelPlaylists.addFile(m_settings.value("File").toString(),
                                 filesHash);
    }

    m_settings.endArray();

    const int listCount = m_modelPlaylists.lists().count();
    //ui->tblPlaylists->resizeColumnsToContents();
    ui->actionRemoveAllPlaylists->setEnabled(listCount > 0);
}

bool MainWindow::checkChanges(Playlist *i_pPlaylist)
{
    if (i_pPlaylist->hasChanges())
    {
        const int retVal = QMessageBox::question(this, QString(),
                                                 QString("Änderungen an '%1'' speichern?").arg(i_pPlaylist->filename()),
                                                 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
        if (retVal == QMessageBox::Yes)
        {
            // Speichern
            return !savePlaylist(i_pPlaylist);
        }
        else if (retVal == QMessageBox::Cancel)
        {
            // Abbrechen
            return true;
        }
    }

    return false;
}

bool MainWindow::savePlaylist(Playlist *i_pPlaylist, bool i_saveAs)
{
    if (i_saveAs)
    {
        const QString& name = QFileDialog::getSaveFileName(this, "Playliste speichern",
                                                           i_pPlaylist->fileInfo().absoluteFilePath(),
                                                           "MPL-Playlisten (*.mpl);;M3U-Playlisten (*.m3u)");

        if (!name.isEmpty())
        {
            if (!i_pPlaylist->setFilepath(name))
            {
                QMessageBox::warning(this, QString(),
                                     QString("Ungültige Dateiendung: %1").
                                     arg(QFileInfo(name).suffix()));
                return false;
            }
        }
        else
            return false;
    }

    if (i_pPlaylist->incompleteCount() > 0)
    {
        const int retVal = QMessageBox::question(this, QString(),
                                                 QString("'%1'' enthält unvollständige oder fehlerhafte Einträge.\nTrotzdem speichern?").arg(i_pPlaylist->filename()));
        if (retVal == QMessageBox::No)
            return false;
    }

    if (i_pPlaylist->save())
        return true;
    else
    {
        QMessageBox::critical(this, QString(),
                              QString("Fehler beim Speichern von:\n%1").arg(i_pPlaylist->fileInfo().absoluteFilePath()));
    }

    return false;
}

void MainWindow::saveSelectedPlaylists(bool i_saveAs)
{
    const QModelIndexList& rows = ui->tblPlaylists->selectionModel()->selectedRows();

    foreach(const QModelIndex& mi, rows)
    {
        const int idx = m_pPlaylistsProxy->mapToSource(mi).row();
        Playlist* pList = m_modelPlaylists.lists().at(idx);

        if (pList->hasChanges() || i_saveAs)
        {
            const bool result = savePlaylist(pList, i_saveAs);

            // Einträge aktualisieren, da Liste evtl. neu sortiert wurde
            if (rows.size() == 1)
                showEntries();

            if (!result)
                break;
        }
    }
}
